/**
 * PHOSIDE: PHosphorylation Site IDentification Engine.
 * Copyright 2009 Digital Biology Lab, University of Missouri.
 * This library is free software; you can redistribute it and/or modify it under
 * the terms of the GNU General Public License as published by the Free Software
 * Foundation; either version 3 of the License, or (at your option) any later
 * version. <p/> This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the License for more
 * details.
 */

package phoside.miscellaneous.io;

import java.io.IOException;

import java.util.List;
import java.util.Set;
import java.util.HashSet;
import java.util.regex.Pattern;
import java.util.regex.Matcher;

import phoside.PhosphoProteins;
import phoside.PhosphoProteinsImpl;
import phoside.Protein;

import phoside.io.ProteinsReader;
import phoside.io.xml.PhosideXmlProteinsReader;
import phoside.io.xml.PhosphoProteinFieldValueFormatter;
import phoside.io.xml.PhosideXmlProteinsWriter;

import phoside.util.IOUtil;

/**
 *
 * @author gjj
 */
public class PhosphoReaderPhosPhAtReport implements ProteinsReader {
    protected class Filter {
        Set<Integer> charges;
        Double ppm;
        Boolean ambiguous;
        Set<String> pubmed;
    }

    protected final String report;
    protected final PhosphoProteins phosphoData;
    protected final Filter filter;

    public PhosphoReaderPhosPhAtReport(final String report,
            final PhosphoProteins phosphoData) {
        if (phosphoData==null) {
            throw new NullPointerException();
        }
        this.report = report;
        this.phosphoData = phosphoData;
        this.filter = new Filter();
    }

    public void setFilter(final int[] charges,
                     final Double ppm,
                     final Boolean ambiguous,
                     final String[] pubmed) {
        if (charges!=null) {
            filter.charges = new HashSet<Integer>();
            for (int ch : charges) {
                filter.charges.add(ch);
            }
        } else {
            filter.charges = null;
        }

        filter.ppm = ppm;

        filter.ambiguous = ambiguous;

        if (pubmed!=null) {
            filter.pubmed = new HashSet<String>();
            for (String pm : pubmed) {
                filter.pubmed.add(pm);
            }
        } else {
            filter.pubmed = null;
        }

    }

    public PhosphoProteins read() throws IOException {
        List<String> strs = IOUtil.readStringListAscii(report);

        int n = strs.size();
        for (int i=1; i<n; i++) {
            String str = strs.get(i);
            String[] ss = str.split("\\t");
            String acc = ss[0];
            String pepseq = ss[1];
            String pepwithmod = ss[2];
            int charge = Integer.parseInt(ss[4]);
            double ppm;
            if (ss.length<7 || ss[6]==null || ss[6].length()==0 ) {
                ppm = Double.POSITIVE_INFINITY;
                System.err.println("line "+i+ ":<7");
            } else if (ss[6].equals("NULL")) {
                ppm = Double.POSITIVE_INFINITY;
            } else {
                ppm = Double.parseDouble(ss[6]);
            }

            String pubmed;
            if (ss.length<10) {
                pubmed="";
                System.err.println("line "+i+ ":<10");
            } else {
                pubmed = ss[9];
            }

            if (filter.charges!=null) {
                if (!filter.charges.contains(charge)) continue;
            }
            if (filter.ppm!=null) {
                if (ppm>filter.ppm) continue;
            }
            if (filter.pubmed!=null) {
                if (!filter.pubmed.contains(pubmed)) continue;
            }

            Protein protein = phosphoData.getProtein(acc);
            if (protein==null) {
                System.err.println("Cannot find protein "+acc);
                continue;
            }

            String proseq = protein.getSequence();


            int peploc = proseq.indexOf(pepseq);
            if (peploc==-1) {
                System.err.println("Protein sequence not match:"+i);
                continue;
            }

            Pattern p = Pattern.compile("\\050([^\\051]+)\\051");
            Matcher m = p.matcher(pepwithmod);
            while (true) {
                if (!m.find()) break;
                String s = m.group(1);
                if (Pattern.matches("p[STY]", s) ) {
                    int site = peploc+m.start(1)-1;
                    try {
                        phosphoData.addSite(acc, site);
                    } catch (IllegalStateException ex) {
                        ex.printStackTrace();
                    }
                } else {
                    if ((filter.ambiguous==null||filter.ambiguous==true)
                            && Pattern.matches("[sty]", s)) {
                        int site = peploc+m.start(1)-1;
                        try {
                            phosphoData.addSite(acc, site);
                        } catch (IllegalStateException ex) {
                            ex.printStackTrace();
                        }
                    }
                }

                m.reset(m.replaceFirst("#"));
            }
        }

        return phosphoData;
    }

    public static void main(String[] args) {
        String dirXml = "D:\\projects\\PHOSIDE\\PHOSIDE\\data\\tair\\TAIR9_pep_20090619.xml";
        String dirPhosphat = "D:\\projects\\PHOSIDE\\PHOSIDE\\data\\phosphat\\phosphat_3_0_exp.txt";
        String dirOut = "D:\\projects\\PHOSIDE\\PHOSIDE\\data\\phosphat\\Arabidopsis-TAIR9-PhosPhAt3-nonambiguous.xml";
//        String dirOut = ".\\data\\TAIR7_PhosPhAt2.2_Sugiyama_No_Ambiguous.fasta";

        PhosphoProteins phosphoData = new PhosphoProteinsImpl();

        PhosphoReaderPhosPhAtReport phosphoReader = new PhosphoReaderPhosPhAtReport(dirPhosphat, phosphoData);
        int[] charges = null;
        //Double ppm = 3.0;
        Boolean ambiguous = false;
        //String[] pubmed = new String[] {"18463617"};
        phosphoReader.setFilter(charges, null, ambiguous, null);

        PhosideXmlProteinsReader proteinsReader = new PhosideXmlProteinsReader(dirXml, phosphoData,
                new PhosphoProteinFieldValueFormatter());

        PhosideXmlProteinsWriter writer = new PhosideXmlProteinsWriter(dirOut, new PhosphoProteinFieldValueFormatter());

        try {
            proteinsReader.read();
            phosphoReader.read();
            writer.write(phosphoData);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
